package com.jhl.seckill.controller;

import cn.hutool.core.util.StrUtil;
import com.jhl.seckill.FeignClient.GoodsClient;
import com.jhl.seckill.FeignClient.MQClient;
import com.jhl.seckill.FeignClient.UserClient;
import com.jhl.seckill.entity.SeckillOrder;
import com.jhl.seckill.entity.User;
import com.jhl.seckill.mapper.OrderMapper;
import com.jhl.seckill.service.OrderService;
import com.jhl.seckill.service.SeckillOrderService;
import com.jhl.seckill.vo.GoodsVo;
import com.jhl.seckill.vo.RespBean;
import com.jhl.seckill.vo.RespBeanEnum;
import com.jhl.seckill.vo.SeckillMsg;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * 秒杀接口也需要登录，因此网关不能放行
 */
@Controller
@RequestMapping("seckill")
@Slf4j
public class SeckillController implements InitializingBean {


    private final Map<Long, Boolean> EmptyStockMap = new HashMap<>();
    @Autowired
    GoodsClient goodsClient;
    @Autowired
    UserClient userClient;
    @Autowired
    OrderService orderService;
    @Autowired
    SeckillOrderService seckillOrderService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    MQClient mqClient;
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    RedisScript redisScript;

    @ApiOperation("获取秒杀地址")
    @GetMapping(value = "/path")
    @ResponseBody
    public RespBean getSeckillPath(Long goodsId, String captcha, HttpServletRequest request) {
        log.info("执行getSeckillPath方法");

//        从请求头获取用户信息
        String username = request.getHeader("user");
        User user = userClient.findUser(username);

//        缓存
        ValueOperations valueOperations = redisTemplate.opsForValue();

//        校验验证码
        boolean check = orderService.checkCaptcha(user, goodsId, captcha);
        if (!check) {
            log.info("验证码验证失败");
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }

//        生成秒杀地址
//        String redisPath = (String) valueOperations.get("seckillPath:" + user.getId());
//        if(redisPath!=null){
//            return RespBean.success(redisPath);
//        }
        String path = orderService.createPath(user, goodsId);
//        缓存秒杀地址
//        valueOperations.set("seckillPath:"+user.getId(),path,30,TimeUnit.SECONDS);
        log.info("生成的秒杀地址=" + path);
        return RespBean.success(path);

    }


    @RequestMapping(value = "/{path}/doSeckill", method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSeckill(@PathVariable String path, Long goodsId, HttpServletRequest request) throws Exception {
//        请求头获取user
        String username = request.getHeader("user");
        User user = userClient.findUser(username);

        if (user == null) {
            log.info("用户未登录");
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        ValueOperations valueOperations = redisTemplate.opsForValue();

//        验证路径
        boolean check = orderService.checkPath(user, goodsId, path);
        if (!check) {
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }

//        防止超卖
        log.info("秒杀路径检查完毕，正在检查是否超卖...");
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);

        if (seckillOrder != null) {

            log.info(user.getUsername() + "已经购买过商品，不能再次购买！");
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
//        内存标记，减少redis的访问
        if (EmptyStockMap.get(goodsId)) {
            log.info("库存不足");
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

//        预减库存
        log.info("redis预减库存");

        /**
         * 使用redis分布式锁进行优化
         */
        Long stock = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId));
//        库存小于0
        if (stock < 0) {
            EmptyStockMap.put(goodsId, true);
            valueOperations.increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

//        创建秒杀消息
        SeckillMsg seckillMsg = new SeckillMsg(user, goodsId);

//        由消息队列执行秒杀逻辑
        log.info("预减成功！准备向消息队列发送下单消息！");
        mqClient.sendTopicSeckillMessage(StrUtil.toString(seckillMsg));
        log.info("准备跳转页面");

        /**
         * 预减成功代表用户实际上已经购买成功
         * 下一步应该跳转到订单页面，由于使用的是thymeleaf，需要手动传入model
         */
        return RespBean.success();

    }

    @ApiOperation("获取秒杀结果")
    @RequestMapping(value = "result", method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(Long goodsId, HttpServletRequest request) {
        String username = request.getHeader("user");
        User user = userClient.findUser(username);
        Long orderId = seckillOrderService.getResult(user, goodsId);
        return RespBean.success(orderId);
    }


    /**
     * 生成验证码
     *
     * @param goodsId  商品ID
     * @param response 响应
     */
    @RequestMapping(value = "/captcha", method = RequestMethod.GET)
    public void verifyCode(Long goodsId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = request.getHeader("user");
        User user = userClient.findUser(username);
//        设置请求头
        response.setContentType("image/jpg");
        response.setHeader("Pargam", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
//        永不失效
        response.setDateHeader("Expires", 0);

        ValueOperations valueOperations = redisTemplate.opsForValue();
//        生成数字验证码
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(120, 32, 3);
//        缓存验证码
        valueOperations.set("captcha:" + user.getId() + ":" + goodsId, captcha.text(), 300, TimeUnit.SECONDS);
        try {
            captcha.out(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 系统初始化，调用商品服务把商品数量加载到缓存
     */
    @Override
    public void afterPropertiesSet() {
        log.info("调用商品服务预加载库存");

        List<GoodsVo> list = goodsClient.findGoodsVo();

        if (CollectionUtils.isEmpty(list)) {
            return;
        }
//        seckillGoods:goods_id:数量
        list.forEach(goodsVo -> {
                    redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());
                    EmptyStockMap.put(goodsVo.getId(), false);
                }
        );
    }
}

